package edu.unipd.p2t.connection;

import java.io.IOException;
import java.util.UUID;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import backport.android.bluetooth.BluetoothAdapter;
import backport.android.bluetooth.BluetoothDevice;
import backport.android.bluetooth.BluetoothServerSocket;
import backport.android.bluetooth.BluetoothSocket;

/**
 * @author Tommaso Dal Sasso
 * 
 */
public class ConnectionManager extends Service {

	public static final UUID P2T_UUID = UUID.fromString( "53ff2249-4343-414d-825d-b3c938384635" );

	public static final int DISCOVERY_PERIOD = 30000;
	/**
	 * Intent codes
	 */
	public static final int REQUEST_ENABLE_BT = 1;

	/**
	 * The number of contemporary connections
	 */
	protected int maxConnections = 5;

	/**
	 * The log tag of the connection manager
	 */
	public final String TAG = "p2t";

	/**
	 * The Bluetooth adapter
	 */
	private BluetoothAdapter btAdapter;

	/**
	 * This is the broadcast receiver that discovers new Bluetooth devices
	 */
	private final BroadcastReceiver myReceiver;

	private LeakyBucket devicePool = new LeakyBucket( maxConnections );

	/**
	 * The listener thread
	 */
	private ServerThread listenServer;

	private static boolean running;


	/**
	 * Private singleton constructor
	 */
	public ConnectionManager() {

		myReceiver = new BroadcastReceiver() {

			@Override
			public void onReceive( Context context, Intent intent ) {
				String action = intent.getAction();

				// found a device:
				if( BluetoothDevice.ACTION_FOUND.equals( action ) ) {
					// get the bluetooth device
					BluetoothDevice device = intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE );
					
					// debug area
					String log = "*** found bluetooth device: " + device.getName();
					Log.d( TAG, log );
					
					// storing the new device in the device pool
//					devicePool.add( new BtDevice( device ) );
					stopDiscovery();
					try{ new BtDevice( device ).connect(); }
					catch( Exception e ) {
						Log.e( TAG, "Exception caught during BtDevice creation: "
								+ e.getMessage(), e );
					}
				}
			}
		};
		Log.d( TAG, "CONSTRUCTOR: ended" );
	};


	@Override
	public void onCreate() {
		super.onCreate();

		Toast.makeText( this, "service created", Toast.LENGTH_SHORT ).show();
		Log.d( TAG, "ConnectionManager.onCreate" );

		// retrieve the Bluetooth adapter
		btAdapter = BluetoothAdapter.getDefaultAdapter();

		if ( btAdapter == null ) {
			// ouch! this device does not support Bluetooth
			Log.d( TAG, "ouch! this device does not support bluetooth" );
		} else {
			Log.d( TAG, "Bluetooth adapter correctly retrieved" );

			if( enableBluetooth() ) {
				running = true;
			} else {
				stopSelf();
				return;
			}

			// register the BroadcastReceiver
			IntentFilter filter = new IntentFilter( BluetoothDevice.ACTION_FOUND );

			if( myReceiver == null )
				Log.d( TAG, "myReceiver is null" );
			else
				Log.d( TAG, "myReceiver correctly instantiated" );

			registerReceiver( myReceiver, filter );
		}
	}

	@Override
	public void onStart(Intent intent, int startId) {

		Log.d( TAG, "ConnectionManager.onStart" );
		Toast.makeText( this, "service started", Toast.LENGTH_SHORT ).show();

		new Thread() {
			
			@Override
			public void run() { //	*** MAIN LOOP START ***

				startListening();

				while ( isRunning() ) {
					
					int activeConnections = BtDevice.getActiveConnections();
					if( activeConnections <= 0 )
						seek();
					else
						Log.d( TAG, "There are " + activeConnections + " active connections: seek canceled" );
					
					try { sleep( DISCOVERY_PERIOD ); }
					catch( InterruptedException ie ) {
						Log.d( TAG, "main loop: Interrupted exception during sleep" );
					}

				}	
			};

		}.start();

	} // onStart


	/**
	 * Enables bluetooth
	 */
	public boolean enableBluetooth() {
		// enabling Bluetooth
		if ( !btAdapter.isEnabled() ) {

			Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE );
			enableBtIntent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );

			startActivity( enableBtIntent );
		}
		return btAdapter.isEnabled();
	}

	/* (non-Javadoc)
	 * @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
	 */
	//	@Override
	//	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	//		if( requestCode == REQUEST_ENABLE_BT ) {
	//			if( resultCode == RESULT_OK ) {
	//				Log.d( TAG, "Bluetooth correctly enabled" );
	//			} else { // RESULT_CANCELED
	//				Log.d(TAG, "unable to activate bluetooth" );
	//			}
	//		}
	//	}



	/* (non-Javadoc)
	 * @see android.app.Service#onDestroy()
	 */
	@Override
	public void onDestroy() {
		super.onDestroy();  // this is needed!
		stopListening(); // stopping main thread
		unregisterReceiver( myReceiver );
		Log.d( TAG, "ConnectionManager.onDestroy" );
		Toast.makeText( this, "service destroyed", Toast.LENGTH_LONG );
		running = false;
	}


	/**
	 * Starts the discovery of new devices
	 * @return
	 */
	public boolean seek() {
		Log.d( TAG, "ConnectionManager.seek(): starting a new discovery" );
		btAdapter.cancelDiscovery();
		return btAdapter.startDiscovery();
	}

	/**
	 * Stops device discovery (BluetoothAdapter.cancelDiscovery() wrapper)
	 */
	public void stopDiscovery() {
		btAdapter.cancelDiscovery();
	}


	private class ServerThread extends Thread {

		private static final String NAME = "p2t";
		public final BluetoothServerSocket listenerServerSocket;

		/**
		 * The stop condition for the main listening loop
		 */
		private boolean stop = false;

		public ServerThread() {
			BluetoothServerSocket tmp = null;

			try {
				tmp = btAdapter.listenUsingRfcommWithServiceRecord( NAME, P2T_UUID );
			} catch( IOException e ) {
				Log.e( TAG, "error in listenUsingRfcommWithServiceRecord" );
			}
			listenerServerSocket = tmp;
		}

		@Override
		public void run() {
			BluetoothSocket socket = null;

			// main listening loop
			Log.d( TAG, "ConnectionManager.ServerThread.run()" );
			while( !stop ) {
				try{
					socket = listenerServerSocket.accept();
				} catch( IOException e ) {
					Log.e( TAG, "main loop: error during accept()" );
				}

				if( socket != null ) {
					// so we have to create a new Device, ecc. ecc. ecc...
					BtDevice incomingDevice = new BtDevice( socket.getRemoteDevice(), socket );
					// we are listening, so we first receive and then transmit
					incomingDevice.setFirst( false );
					incomingDevice.start();
				}
			}
			Log.d( TAG, "ConnectionManager.ServerThread.run(): exiting" );
		}


		public void cancel() {
			stop = true;

			try{
				listenerServerSocket.close();
			} catch( IOException e ) {
				Log.e(TAG, "error closing listener thread" );
			}
		}

	}

	/**
	 * Starts the listener server that waits for new incoming connections
	 */
	public void startListening() {
		Log.d( TAG, "ConnectionManager.startListening()" );
		listenServer = new ServerThread();
		listenServer.start();
	}

	public void stopListening() {
		Log.d( TAG, "ConnectionManager.stopListening()" );
		listenServer.cancel();
	}

	@Override
	public IBinder onBind(Intent arg0) {
		// Please, ignore this one
		Log.d( TAG, "ConnectionManager.onBind" );
		return null;
	}


	public static boolean isRunning() {
		return running;
	}
	
}
